#include "stdafx.h"
#include "net/IPAddress.hpp"
#include "charset/string.hpp"
using JHCPP::charset::trim;

NET_NAMESPACE_BEGIN

CIPAddress::CIPAddress()
{
	newIPv4();
}

CIPAddress::CIPAddress(const CIPAddress& addr)
{
	if (addr.family() == IPv4)
		newIPv4(addr.addr());
	else
		newIPv6(addr.addr(), addr.scope());
}

CIPAddress::CIPAddress(Family family)
{
	if (family == IPv4)
		newIPv4();
#if defined(JH_HAVE_IPv6)
	else if (family == IPv6)
		newIPv6();
#endif
	else
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress::CIPAddress(const std::string& addr)
{
	CIPv4AddressImpl empty4 = CIPv4AddressImpl();
	if (addr.empty() || trim(addr) == "0.0.0.0")
	{
		newIPv4(empty4.addr());
		return;
	}

	CIPv4AddressImpl addr4(CIPv4AddressImpl::parse(addr));
	if (addr4 != empty4)
	{
		newIPv4(addr4.addr());
		return;
	}

#if defined(JH_HAVE_IPv6)
	CIPv6AddressImpl empty6 = CIPv6AddressImpl();
	if (addr.empty() || trim(addr) == "::")
	{
		newIPv6(empty6.addr());
		return;
	}

	CIPv6AddressImpl addr6(CIPv6AddressImpl::parse(addr));
	if (addr6 != CIPv6AddressImpl())
	{
		newIPv6(addr6.addr(), addr6.scope());
		return;
	}
#endif

	throw InvalidAddressException(addr);
}

CIPAddress::CIPAddress(const std::string& addr, Family family)
{
	if (family == IPv4)
	{
		CIPv4AddressImpl addr4(CIPv4AddressImpl::parse(addr));
		newIPv4(addr4.addr());
		return;
	}
#if defined(JH_HAVE_IPv6)
	else if (family == IPv6)
	{
		CIPv6AddressImpl addr6(CIPv6AddressImpl::parse(addr));
		newIPv6(addr6.addr(), addr6.scope());
		return;
	}
#endif
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress::CIPAddress(const void* addr, int length) : m_pImpl(0)
{
	if (length == sizeof(struct in_addr))
		newIPv4(addr);
	#if defined(JH_HAVE_IPv6)
	else if (length == sizeof(struct in6_addr))
		newIPv6(addr);
	#endif
	else
		throw InvalidArgumentException("Invalid address length passed to IPAddress()");
}

CIPAddress::CIPAddress(const void* addr, int length, UInt32 scope)
{
	if (length == sizeof(struct in_addr))
		newIPv4(addr);
	#if defined(JH_HAVE_IPv6)
	else if (length == sizeof(struct in6_addr))
		newIPv6(addr, scope);
	#endif
	else 
		throw InvalidArgumentException("Invalid address length passed to IPAddress()");
}

CIPAddress::CIPAddress(unsigned prefix, Family family)
{
	if (family == IPv4)
	{
		if (prefix <= 32)
			newIPv4(prefix);
		else
			throw InvalidArgumentException("Invalid prefix length passed to IPAddress()");
	}
#if defined(JH_HAVE_IPv6)
	else if (family == IPv6)
	{
		if (prefix <= 128)
			newIPv6(prefix);
		else
			throw InvalidArgumentException("Invalid prefix length passed to IPAddress()");
	}
#endif
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

#if defined(_WIN32)
CIPAddress::CIPAddress(const SOCKET_ADDRESS& socket_address) : m_pImpl(0)
{
	ADDRESS_FAMILY family = socket_address.lpSockaddr->sa_family;
	if (family == AF_INET)
		newIPv4(&reinterpret_cast<const struct sockaddr_in*>(socket_address.lpSockaddr)->sin_addr);
	#if defined(JH_HAVE_IPv6)
	else if (family == AF_INET6)
		newIPv6(&reinterpret_cast<const struct sockaddr_in6*>(socket_address.lpSockaddr)->sin6_addr,
		reinterpret_cast<const struct sockaddr_in6*>(socket_address.lpSockaddr)->sin6_scope_id);
	#endif
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}
#endif

CIPAddress::CIPAddress(const struct sockaddr& sockaddr)
{
	unsigned short family = sockaddr.sa_family;
	if (family == AF_INET)
		newIPv4(&reinterpret_cast<const struct sockaddr_in*>(&sockaddr)->sin_addr);
	#if defined(JH_HAVE_IPv6)
	else if (family == AF_INET6)
		newIPv6(&reinterpret_cast<const struct sockaddr_in6*>(&sockaddr)->sin6_addr,
		reinterpret_cast<const struct sockaddr_in6*>(&sockaddr)->sin6_scope_id);
	#endif
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress::~CIPAddress()
{
	destruct();
}

CIPAddress& CIPAddress::operator = (const CIPAddress& addr)
{
	if (&addr != this)
	{
		destruct();
		if (addr.family() == CIPAddress::IPv4)
			newIPv4(addr.addr());
		else
			newIPv6(addr.addr(), addr.scope());
	}
	return *this;
}

bool CIPAddress::operator == (const CIPAddress& a) const
{
	int l1 = length();
	int l2 = a.length();
	if (l1 == l2)
	{
	#if defined(JH_HAVE_IPv6)
		if ( scope() != a.scope() )
			return false;
	#endif
		return std::memcmp(addr(), a.addr(), l1) == 0;
	}
	else return false;
}

bool CIPAddress::operator != (const CIPAddress& a) const
{
	return !(*this == a);
}

bool CIPAddress::operator < (const CIPAddress& a) const
{
	int l1 = length();
	int l2 = a.length();
	if (l1 == l2)
	{
#if defined(JH_HAVE_IPv6)
		if ( scope() != a.scope() )
			return scope() < a.scope();
#endif
		return std::memcmp(addr(), a.addr(), l1) < 0;
	}
	else return l1 < l2;
}

bool CIPAddress::operator <= (const CIPAddress& a) const
{
	return !(a < *this);
}

bool CIPAddress::operator > (const CIPAddress& a) const
{
	return a < *this;
}

bool CIPAddress::operator >= (const CIPAddress& a) const
{
	return !(*this < a);
}

CIPAddress CIPAddress::operator & (const CIPAddress& other) const
{
	if (family() == other.family())
	{
		if (family() == IPv4)
		{
			CIPv4AddressImpl t(pImpl()->addr());
			CIPv4AddressImpl o(other.pImpl()->addr());
			return CIPAddress((t & o).addr(), sizeof(struct in_addr));
		}
	#if defined(JH_HAVE_IPv6)
		else if (family() == IPv6)
		{
			const CIPv6AddressImpl t(pImpl()->addr(), pImpl()->scope());
			const CIPv6AddressImpl o(other.pImpl()->addr(), other.pImpl()->scope());
			const CIPv6AddressImpl r = t & o;
			return CIPAddress(r.addr(), r.scope(), sizeof(struct in6_addr));
		}
	#endif
		else 
			throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
	}
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress CIPAddress::operator | (const CIPAddress& other) const
{
	if (family() == other.family())
	{
		if (family() == IPv4)
		{
			CIPv4AddressImpl t(pImpl()->addr());
			CIPv4AddressImpl o(other.pImpl()->addr());
			return CIPAddress((t | o).addr(), sizeof(struct in_addr));
		}
	#if defined(JH_HAVE_IPv6)
		else if (family() == IPv6)
		{
			const CIPv6AddressImpl t(pImpl()->addr(), pImpl()->scope());
			const CIPv6AddressImpl o(other.pImpl()->addr(), other.pImpl()->scope());
			const CIPv6AddressImpl r = t | o;
			return CIPAddress(r.addr(), r.scope(), sizeof(struct in6_addr));
		}
	#endif
		else 
			throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
	}
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress CIPAddress::operator ^ (const CIPAddress& other) const
{
	if (family() == other.family())
	{
		if (family() == IPv4)
		{
			CIPv4AddressImpl t(pImpl()->addr());
			CIPv4AddressImpl o(other.pImpl()->addr());
			return CIPAddress((t ^ o).addr(), sizeof(struct in_addr));
		}
	#if defined(JH_HAVE_IPv6)
		else if (family() == IPv6)
		{
			const CIPv6AddressImpl t(pImpl()->addr(), pImpl()->scope());
			const CIPv6AddressImpl o(other.pImpl()->addr(), other.pImpl()->scope());
			const CIPv6AddressImpl r = t ^ o;
			return CIPAddress(r.addr(), r.scope(), sizeof(struct in6_addr));
		}
	#endif
		else 
			throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
	}
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress CIPAddress::operator ~ () const
{
	if (family() == IPv4)
	{
		CIPv4AddressImpl self(this->pImpl()->addr());
		return CIPAddress((~self).addr(), sizeof(struct in_addr));
	}
#if defined(JH_HAVE_IPv6)
	else if (family() == IPv6)
	{
		const CIPv6AddressImpl self(pImpl()->addr(), pImpl()->scope());
		const CIPv6AddressImpl r = ~self;
		return CIPAddress(r.addr(), sizeof(struct in6_addr), r.scope());
	}
#endif
	else 
		throw InvalidArgumentException("Invalid or unsupported address family passed to IPAddress()");
}

CIPAddress::Family CIPAddress::family() const
{
	return static_cast<CIPAddress::Family>(pImpl()->family());
}

UInt32 CIPAddress::scope() const
{
	return pImpl()->scope();
}

std::string CIPAddress::toString() const
{
	return pImpl()->toString();
}

bool CIPAddress::isWildcard() const
{
	return pImpl()->isWildcard();
}

bool CIPAddress::isBroadcast() const
{
	return pImpl()->isBroadcast();
}

bool CIPAddress::isLoopback() const
{
	return pImpl()->isLoopback();
}

bool CIPAddress::isMulticast() const
{
	return pImpl()->isMulticast();
}

bool CIPAddress::isUnicast() const
{
	return !isWildcard() && !isBroadcast() && !isMulticast();
}

bool CIPAddress::isLinkLocal() const
{
	return pImpl()->isLinkLocal();
}

bool CIPAddress::isSiteLocal() const
{
	return pImpl()->isSiteLocal();
}

bool CIPAddress::isIPv4Compatible() const
{
	return pImpl()->isIPv4Compatible();
}

bool CIPAddress::isIPv4Mapped() const
{
	return pImpl()->isIPv4Mapped();
}

bool CIPAddress::isWellKnownMC() const
{
	return pImpl()->isWellKnownMC();
}

bool CIPAddress::isNodeLocalMC() const
{
	return pImpl()->isNodeLocalMC();
}

bool CIPAddress::isLinkLocalMC() const
{
	return pImpl()->isLinkLocalMC();
}

bool CIPAddress::isSiteLocalMC() const
{
	return pImpl()->isSiteLocalMC();
}

bool CIPAddress::isOrgLocalMC() const
{
	return pImpl()->isOrgLocalMC();
}

bool CIPAddress::isGlobalMC() const
{
	return pImpl()->isGlobalMC();
}

JH_SOCKETLEN CIPAddress::length() const
{
	return pImpl()->length();
}

const void* CIPAddress::addr() const
{
	return pImpl()->addr();
}

int CIPAddress::af() const
{
	return pImpl()->af();
}

unsigned CIPAddress::prefixLength() const
{
	return pImpl()->prefixLength();
}

void CIPAddress::mask(const CIPAddress& mask)
{
	CIPAddress null;
	pImpl()->mask(mask.pImpl(), null.pImpl());
}


void CIPAddress::mask(const CIPAddress& mask, const CIPAddress& set)
{
	pImpl()->mask(mask.pImpl(), set.pImpl());
}

CIPAddress CIPAddress::parse(const std::string& addr)
{
	return CIPAddress(addr);
}

bool CIPAddress::tryParse(const std::string& addr, CIPAddress& result)
{
	CIPv4AddressImpl impl4(CIPv4AddressImpl::parse(addr));
	if (impl4 != CIPv4AddressImpl() || trim(addr) == "0.0.0.0")
	{
		result.newIPv4(impl4.addr());
		return true;
	}
#if defined(JH_HAVE_IPv6)
	CIPv6AddressImpl impl6(CIPv6AddressImpl::parse(addr));
	if (impl6 != CIPv6AddressImpl())
	{
		result.newIPv6(impl6.addr(), impl6.scope());
		return true;
	}
#endif
	return false;
}

CIPAddress CIPAddress::wildcard(Family family)
{
	return CIPAddress(family);
}

CIPAddress CIPAddress::broadcast()
{
	struct in_addr ia;
	ia.s_addr = INADDR_NONE;
	return CIPAddress(&ia, sizeof(ia));
}

CIPAddress::Ptr CIPAddress::pImpl() const
{
	if (m_pImpl) 
		return m_pImpl;
	throw NullPointerException("IPaddress implementation pointer is NULL.");
}

void CIPAddress::newIPv4(const void* hostAddr)
{
	m_pImpl = new CIPv4AddressImpl(hostAddr);
}

void CIPAddress::newIPv6(const void* hostAddr)
{
	m_pImpl = new CIPv6AddressImpl(hostAddr);
}

void CIPAddress::newIPv6(const void* hostAddr, UInt32 scope)
{
	m_pImpl = new CIPv6AddressImpl(hostAddr, scope);
}

void CIPAddress::newIPv4(unsigned prefix)
{
	m_pImpl = new CIPv4AddressImpl(prefix);
}

void CIPAddress::newIPv6(unsigned prefix)
{
	m_pImpl = new CIPv6AddressImpl(prefix);
}

void CIPAddress::newIPv4()
{
	m_pImpl = new CIPv4AddressImpl;
}

void CIPAddress::newIPv6()
{
	m_pImpl = new CIPv6AddressImpl;
}

void CIPAddress::destruct()
{
	pImpl()->~CIPAddressImpl();
}

CBinaryWriter& operator << (CBinaryWriter& writer, const CIPAddress& value)
{
	writer.stream().write((const char*) value.addr(), value.length());
	return writer;
}

CBinaryReader& operator >> (CBinaryReader& reader, CIPAddress& value)
{
	char buf[sizeof(struct in6_addr)];
	reader.stream().read(buf, value.length());
	value = CIPAddress(buf, value.length());
	return reader;
}

NET_NAMESPACE_END
